#include <cassert>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <fcntl.h>
#include <stdexcept>
#include <string>
#include <fstream>
#include <iostream>
#include <limits.h>
#include <syslog.h>
#include <unistd.h>
#include <unordered_map>

#include "types.h"
#include "bigBuffer.h"
#include "vsphere_handle.h"
#include "huawei_handle.h"
#include "config.h"
#include "util.h"

extern struct vmfuse_param param;
/**
 * Class that keep chunk of file data.
 */

class BigBuffer::ChunkWrapper
{
private:
    /**
     * Pointer that keeps data for chunk. Can be NULL.
     */
    char *m_ptr;
    bool used_flag;

public:
    /**
     * By default internal buffer is NULL, so this can be used for creating
     * sparse files.
     */
    ChunkWrapper() : m_ptr(NULL), used_flag(false)
    {
    }

    /**
     * Take ownership on internal pointer from 'other' object.
     */
    ChunkWrapper(const ChunkWrapper &other)
    {
        m_ptr = other.m_ptr;
        used_flag = other.used_flag;
        const_cast<ChunkWrapper *>(&other)->m_ptr = NULL;
        const_cast<ChunkWrapper *>(&other)->used_flag = false;
    }

    /**
     * Free pointer if allocated.
     */
    ~ChunkWrapper()
    {
        if (m_ptr != NULL)
        {
            free(m_ptr);
        }
    }

    /**
     * Take ownership on internal pointer from 'other' object.
     */
    ChunkWrapper &operator=(const ChunkWrapper &other)
    {
        if (&other != this)
        {
            m_ptr = other.m_ptr;
            used_flag = other.used_flag;
            const_cast<ChunkWrapper *>(&other)->m_ptr = NULL;
            const_cast<ChunkWrapper *>(&other)->used_flag = false;
        }
        return *this;
    }

    /**
     * Return pointer to internal storage and initialize it if needed.
     * @throws
     *      std::bad_alloc  If memory can not be allocated
     */
    char *ptr(bool init = false)
    {
        if (init && m_ptr == NULL)
        {
            m_ptr = (char *)malloc(chunkSize);
            if (m_ptr == NULL)
            {
                throw std::bad_alloc();
            }
        }
        return m_ptr;
    }

    /**
     * Fill 'dest' with internal buffer content.
     * If m_ptr is NULL, destination bytes is zeroed.
     *
     * @param dest      Destination buffer.
     * @param offset    Offset in internal buffer to start reading from.
     * @param count     Number of bytes to be read.
     *
     * @return  Number of bytes actually read. It can differ with 'count'
     *      if offset+count>chunkSize.
     */
    size_t read(char *dest, unsigned int offset, size_t count) const
    {
        if (offset + count > chunkSize)
        {
            count = chunkSize - offset;
        }
        if (m_ptr != NULL)
        {
            memcpy(dest, m_ptr + offset, count);
        }
        else
        {
            memset(dest, 0, count);
        }
        return count;
    }

    size_t file_read(char *dest, unsigned int offset, size_t count, std::fstream &real_file, uint64_t real_offset, uint64_t real_file_size, const char *real_name) const
    {
        if (offset + count > chunkSize)
        {
            count = chunkSize - offset;
        }
        if (m_ptr != NULL)
        {
            memcpy(dest, m_ptr + offset, count);
        }
        else
        {
            if (real_offset < real_file_size)
            {
                size_t read_size = real_file_size >= (real_offset + count) ? count : real_file_size - real_offset;
                real_file.seekg(static_cast<int64_t>(real_offset), std::ios::beg);
                real_file.read(dest, static_cast<int64_t>(read_size));
                if(!real_file)
                {
                    syslog(LOG_ERR, "failed to read %s, get %ld bytes, expected %lu bytes", real_name, real_file.gcount(), read_size);
                    fprintf(stderr, "%s:failed to read %s, get %ld bytes, expected %lu bytes\n", PROGRAM, real_name, real_file.gcount(), read_size);
                }
                if (read_size < count)
                {
                    memset(dest + read_size, 0, count - read_size);
                }
            }
            else
            {
                memset(dest, 0, count);
            }
        }
        return count;
    }

    size_t vsphere_read(char *dest, unsigned int offset, size_t count, std::vector<int>& fd_vector, uint64_t real_offset, uint64_t real_file_size, int store_file, const std::unordered_map<int, std::vector<DataSegment>>& cache) const
    {
        if (offset + count > chunkSize)
        {
            count = chunkSize - offset;
        }
        if(param.memoryFlag)
        {
            if (m_ptr != NULL)
            {
                memcpy(dest, m_ptr + offset, count);
            }
            else
            {
                if (real_offset < real_file_size)
                {
                    size_t read_size = real_file_size >= (real_offset + count) ? count : real_file_size - real_offset;
                    read_vsphere_disk_data(fd_vector, dest, (off_t)real_offset, read_size, cache);
                    if (read_size < count)
                    {
                        memset(dest + read_size, 0, count - read_size);
                    }
                }
                else
                {
                    memset(dest, 0, count);
                }
            }
        }
        else
        {
            if ( used_flag == true )
            {
                pread(store_file, dest, count, (off_t)real_offset);
            }
            else
            {
                if (real_offset < real_file_size)
                {
                    size_t read_size = real_file_size >= (real_offset + count) ? count : real_file_size - real_offset;
                    read_vsphere_disk_data(fd_vector, dest, (off_t)real_offset, read_size, cache);
                    if (read_size < count)
                    {
                        memset(dest + read_size, 0, count - read_size);
                    }
                }
                else
                {
                    memset(dest, 0, count);
                }
            }
        }
        return count;
    }

    size_t fsphere_read(char *dest, unsigned int offset, size_t count, std::vector<int>& fd_vector, uint64_t real_offset, uint64_t real_file_size, int store_file) const
    {
        if (offset + count > chunkSize)
        {
            count = chunkSize - offset;
        }
        if(param.memoryFlag)
        {
            if (m_ptr != NULL)
            {
                memcpy(dest, m_ptr + offset, count);
            }
            else
            {
                if (real_offset < real_file_size)
                {
                    size_t read_size = real_file_size >= (real_offset + count) ? count : real_file_size - real_offset;
                    huawei_read_disk_data(fd_vector, dest, (off_t)real_offset, read_size);
                    if (read_size < count)
                    {
                        memset(dest + read_size, 0, count - read_size);
                    }
                }
                else
                {
                    memset(dest, 0, count);
                }
            }
        }
        else
        {
            if ( used_flag == true )
            {
                pread(store_file, dest, count, (off_t)real_offset);
            }
            else
            {
                if (real_offset < real_file_size)
                {
                    size_t read_size = real_file_size >= (real_offset + count) ? count : real_file_size - real_offset;
                    huawei_read_disk_data(fd_vector, dest, (off_t)real_offset, read_size);
                    if (read_size < count)
                    {
                        memset(dest + read_size, 0, count - read_size);
                    }
                }
                else
                {
                    memset(dest, 0, count);
                }
            }
        }
        return count;
    }

    /**
     * Fill internal buffer with bytes from 'src'.
     * If m_ptr is NULL, memory for buffer is malloc()-ed and then head of
     * allocated space is zeroed. After that byte copying is performed.
     *
     * @param src       Source buffer.
     * @param offset    Offset in internal buffer to start writting from.
     * @param count     Number of bytes to be written.
     *
     * @return  Number of bytes actually written. It can differ with
     *      'count' if offset+count>chunkSize.
     * @throws
     *      std::bad_alloc  If there are no memory for buffer
     */
    size_t write(const char *src, unsigned int offset, size_t count)
    {
        if (offset + count > chunkSize)
        {
            count = chunkSize - offset;
        }
        if (m_ptr == NULL)
        {
            m_ptr = (char *)malloc(chunkSize);
            if (m_ptr == NULL)
            {
                throw std::bad_alloc();
            }
            if (offset > 0)
            {
                memset(m_ptr, 0, offset);
            }
        }
        memcpy(m_ptr + offset, src, count);
        return count;
    }

    size_t file_write(const char *src, unsigned int offset, size_t count, std::fstream &real_file, uint64_t real_offset, uint64_t real_file_size, const char *real_name)
    {
        char buffer[chunkSize+1];
        if (offset + count > chunkSize)
        {
            count = chunkSize - offset;
        }
        if (m_ptr == NULL)
        {
            m_ptr = (char *)malloc(chunkSize);
            if (m_ptr == NULL)
            {
                throw std::bad_alloc();
            }
            if (offset > 0)
            {
                memset(m_ptr, 0, offset);
            }
            if (real_offset < real_file_size)
            {
                size_t read_size = real_file_size >= (real_offset + chunkSize) ? chunkSize : real_file_size - real_offset;
                real_file.seekg(static_cast<int64_t>(real_offset), std::ios::beg);
                real_file.read(buffer, static_cast<int64_t>(read_size));
                if(!real_file)
                {
                    syslog(LOG_ERR, "failed to read %s, get %ld bytes, expected %lu bytes", real_name, real_file.gcount(), read_size);
                    fprintf(stderr, "%s:failed to read %s, get %ld bytes, expected %lu bytes\n", PROGRAM, real_name, real_file.gcount(), read_size);
                }
                if (read_size < chunkSize)
                {
                    memset(buffer + read_size, 0, chunkSize - read_size);
                }
                memcpy(m_ptr, buffer, chunkSize);
            }
        }
        memcpy(m_ptr + offset, src, count);
        return count;
    }

    size_t vsphere_write(const char *src, unsigned int offset, size_t count, std::vector<int>& fd_vector, uint64_t real_offset, uint64_t real_file_size, int store_file, const std::unordered_map<int, std::vector<DataSegment>>& cache)
    {
        char buffer[chunkSize+1] = {0};
        if (offset + count > chunkSize)
        {
            count = chunkSize - offset;
        }
        if(param.memoryFlag)
        {
            if (m_ptr == NULL)
            {
                m_ptr = (char *)malloc(chunkSize);
                if (m_ptr == NULL)
                {
                    throw std::bad_alloc();
                }
                if (offset > 0)
                {
                    memset(m_ptr, 0, offset);
                }
                if (real_offset < real_file_size)
                {
                    size_t read_size = real_file_size >= (real_offset + chunkSize) ? chunkSize : real_file_size - real_offset;
                    read_vsphere_disk_data(fd_vector, buffer, (off_t)real_offset, read_size, cache);
                    if (read_size < chunkSize)
                    {
                        memset(buffer + read_size, 0, chunkSize - read_size);
                    }
                    memcpy(m_ptr, buffer, chunkSize);
                }
            }
            memcpy(m_ptr + offset, src, count);
        }
        else
        {
            if ( used_flag == false )
            {
                used_flag = true;
                if (real_offset < real_file_size)
                {
                    size_t read_size = real_file_size >= (real_offset + chunkSize) ? chunkSize : real_file_size - real_offset;
                    read_vsphere_disk_data(fd_vector, buffer, (off_t)real_offset, read_size, cache);
                    if (read_size < chunkSize)
                    {
                        memset(buffer + read_size, 0, chunkSize - read_size);
                    }
                    pwrite(store_file, buffer, read_size, (off_t)real_offset);
                }
            }
            pwrite(store_file, src, count, (off_t)(real_offset+offset));
        }
        return count;
    }

    size_t fsphere_write(const char *src, unsigned int offset, size_t count, std::vector<int>& fd_vector, uint64_t real_offset, uint64_t real_file_size, int store_file)
    {
        char buffer[chunkSize+1] = {0};
        if (offset + count > chunkSize)
        {
            count = chunkSize - offset;
        }
        if(param.memoryFlag)
        {
            if (m_ptr == NULL)
            {
                m_ptr = (char *)malloc(chunkSize);
                if (m_ptr == NULL)
                {
                    throw std::bad_alloc();
                }
                if (offset > 0)
                {
                    memset(m_ptr, 0, offset);
                }
                if (real_offset < real_file_size)
                {
                    size_t read_size = real_file_size >= (real_offset + chunkSize) ? chunkSize : real_file_size - real_offset;
                    huawei_read_disk_data(fd_vector, buffer, (off_t)real_offset, read_size);
                    if (read_size < chunkSize)
                    {
                        memset(buffer + read_size, 0, chunkSize - read_size);
                    }
                    memcpy(m_ptr, buffer, chunkSize);
                }
            }
            memcpy(m_ptr + offset, src, count);
        }
        else
        {
            if ( used_flag == false )
            {
                used_flag = true;
                if (real_offset < real_file_size)
                {
                    size_t read_size = real_file_size >= (real_offset + chunkSize) ? chunkSize : real_file_size - real_offset;
                    huawei_read_disk_data(fd_vector, buffer, (off_t)real_offset, read_size);
                    if (read_size < chunkSize)
                    {
                        memset(buffer + read_size, 0, chunkSize - read_size);
                    }
                    pwrite(store_file, buffer, read_size, (off_t)real_offset);
                }
            }
            pwrite(store_file, src, count, (off_t)(real_offset+offset));
        }
        return count;
    }

    /**
     * Clear tail of internal buffer with zeroes starting from 'offset'.
     */
    void clearTail(unsigned int offset)
    {
        if (m_ptr != NULL && offset < chunkSize)
        {
            memset(m_ptr + offset, 0, chunkSize - offset);
        }
    }
};

BigBuffer::BigBuffer() : len(0)
{
}

BigBuffer::BigBuffer(size_t length) : len(length)
{
    size_t ccount = chunksCount(length);
    chunks.resize(ccount, ChunkWrapper());
}

void BigBuffer::truncate(size_t offset)
{
    chunks.resize(chunksCount(offset));

    if (offset > len && chunkNumber(len) < chunksCount(len))
    {
        // Fill end of last non-empty chunk with zeroes
        chunks[chunkNumber(len)].clearTail(chunkOffset(len));
    }

    len = offset;
}

BigBuffer::~BigBuffer()
{
}

int BigBuffer::open_disk_files(const char *real_name, std::vector<int>& fd_vector) const
{
    std::string full_name = real_name;
    std::string only_name = "";
    std::string only_path = "";
    size_t pos = full_name.find_last_of('/');
    if(pos == std::string::npos){
        only_name = full_name;
        only_path = "";
    } else {
        only_name = full_name.substr(pos+1);
        only_path = full_name.substr(0, pos);
    }
    if(strlen(param.backupFilelist) && strlen(param.backupDisklist) && strstr(param.backupDisklist, only_name.c_str()))
    {
        char fileNamelist[1024] = {0};
        char *token;
        strcpy(fileNamelist, param.backupFilelist);
        token = strtok(fileNamelist, ",");
        while(token != NULL)
        {
            char fileName[512] = {0};
            sprintf(fileName, "%s/%s", token, only_name.c_str());
            int fd = open(fileName, O_RDONLY);
            if (fd < 0)
            {
                syslog(LOG_ERR, "failed to open %s,with %s", fileName, strerror(errno));
                fprintf(stderr, "%s:failed to open %s,with %s\n", PROGRAM, fileName, strerror(errno));
                return -1;
            }
            fd_vector.push_back(fd);
            token = strtok(NULL, ",");
        }
    }
    else
    {
        int fd = open(real_name, O_RDONLY);
        if (fd < 0)
        {
            syslog(LOG_ERR, "failed to open %s,with %s", real_name, strerror(errno));
            fprintf(stderr, "%s:failed to open %s,with %s\n", PROGRAM, real_name, strerror(errno));
            return -1;
        }
        fd_vector.push_back(fd);
    }
    if(fd_vector.empty())
    {
        syslog(LOG_ERR, "not found any disk file to open %s", real_name);
        fprintf(stderr, "%s:not found any disk file to open %s\n", PROGRAM, real_name);
        return -1;
    }
    std::reverse(fd_vector.begin(), fd_vector.end());
    return static_cast<int>(fd_vector.size());
}

int BigBuffer::read(char *buf, size_t size, size_t offset) const
{
    if (offset > len)
    {
        return 0;
    }
    size_t chunk = chunkNumber(offset);
    unsigned int pos = chunkOffset(offset);
    if (size > len - offset)
    {
        size = len - offset;
    }
    if (size > INT_MAX)
        size = INT_MAX;
    int nread = static_cast<int>(size);
    while (size > 0)
    {
        size_t r = chunks[chunk].read(buf, pos, size);

        size -= r;
        buf += r;
        ++chunk;
        pos = 0;
    }
    return nread;
}

int BigBuffer::file_read(char *buf, size_t size, size_t offset, const char *real_name, uint64_t real_file_size) const
{
    if (offset > len)
    {
        return 0;
    }
    size_t chunk = chunkNumber(offset);
    unsigned int pos = chunkOffset(offset);
    if (size > len - offset)
    {
        size = len - offset;
    }
    if (size > INT_MAX)
        size = INT_MAX;
    int nread = static_cast<int>(size);
    std::fstream real_file;
    real_file.open(real_name, std::ios::binary | std::ios::in);
    if (!real_file.is_open())
    {
        syslog(LOG_ERR, "failed to open %s", real_name);
        fprintf(stderr, "%s:failed to open %s\n", PROGRAM, real_name);
        return 0;
    }
    while (size > 0)
    {
        uint64_t real_offset = static_cast<uint64_t>(chunk * chunkSize + pos);
        size_t r = chunks[chunk].file_read(buf, pos, size, real_file, real_offset, real_file_size, real_name);

        size -= r;
        buf += r;
        ++chunk;
        pos = 0;
    }
    real_file.close();
    return nread;
}

int BigBuffer::vsphere_read(char *buf, size_t size, size_t offset, uint64_t real_file_size, std::vector<int> &real_file_vector, const int store_file, const std::unordered_map<int, std::vector<DataSegment>>& cache) const
{
    //syslog(LOG_ERR, "[%ld]vspehere_read:%s at offset %lu size %lu\n",usecTime(), real_name, offset, size);
    if (offset > len)
    {
        return 0;
    }
    size_t chunk = chunkNumber(offset);
    unsigned int pos = chunkOffset(offset);
    if (size > len - offset)
    {
        size = len - offset;
    }
    if (size > INT_MAX)
        size = INT_MAX;
    int nread = static_cast<int>(size);

    while (size > 0)
    {
        uint64_t real_offset = static_cast<uint64_t>(chunk * chunkSize + pos);
        size_t r = chunks[chunk].vsphere_read(buf, pos, size, real_file_vector, real_offset, real_file_size, store_file, cache);

        size -= r;
        buf += r;
        ++chunk;
        pos = 0;
    }

    //syslog(LOG_ERR, "[%ld]end read: %d bytes from %s at offset %lu size %lu\n",usecTime(),nread, real_name, offset, size);
    return nread;
}

int BigBuffer::fsphere_read(char *buf, size_t size, size_t offset, const char *real_name, uint64_t real_file_size, const char *store_name) const
{
    if (offset > len)
    {
        return 0;
    }
    size_t chunk = chunkNumber(offset);
    unsigned int pos = chunkOffset(offset);
    if (size > len - offset)
    {
        size = len - offset;
    }
    if (size > INT_MAX)
        size = INT_MAX;
    int nread = static_cast<int>(size);

    std::vector<int> real_file_vector;
    int real_file_count = open_disk_files(real_name, real_file_vector);
    if (real_file_count <= 0)
    {
        syslog(LOG_ERR, "fsphere_write:failed to open real_name %s", real_name);
        fprintf(stderr, "%s:fsphere_write:failed to open real_name %s\n", PROGRAM, real_name);
        return 0;
    }
    /*
    int real_file;
    real_file = open(real_name, O_RDONLY);
    if (real_file < 0)
    {
        syslog(LOG_ERR, "fsphere_read:failed to open real_name %s,with %s", real_name, strerror(errno));
        fprintf(stderr, "%s:fsphere_read:failed to open real_name %s,with %s\n", PROGRAM, real_name, strerror(errno));
        return 0;
    }
    */
    int store_file = 0;
    if(!param.memoryFlag)
    {
        if(strlen(param.storeDir) == 0)
        {
            syslog(LOG_ERR, "storeDir is not set");
            fprintf(stderr, "%s:storeDir is not set\n", PROGRAM);
            return 0;
        }
        if(strlen(store_name) == 0 || access(store_name, F_OK) != 0)
        {
            syslog(LOG_ERR, "failed to access %s", store_name);
            fprintf(stderr, "%s:failed to access %s\n", PROGRAM, store_name);
            return 0;
        }
        store_file = open(store_name, O_RDONLY);
        if (store_file < 0)
        {
            syslog(LOG_ERR, "fsphere_read:failed to open store_name %s,with %s", store_name, strerror(errno));
            fprintf(stderr, "%s:fsphere_read:failed to open store_name %s,with %s\n", PROGRAM, store_name, strerror(errno));
            return 0;
        }
    }
    while (size > 0)
    {
        uint64_t real_offset = static_cast<uint64_t>(chunk * chunkSize + pos);
        size_t r = chunks[chunk].fsphere_read(buf, pos, size, real_file_vector, real_offset, real_file_size, store_file);

        size -= r;
        buf += r;
        ++chunk;
        pos = 0;
    }
    for(int fd : real_file_vector)
    {
        if(fd > 0)
            close(fd);
    }
    /*
    if(real_file > 0)
        close(real_file);
    */
    if(store_file > 0)
        close(store_file);
    return nread;
}

int BigBuffer::write(const char *buf, size_t size, size_t offset)
{
    size_t chunk = chunkNumber(offset);
    unsigned int pos = chunkOffset(offset);
    if (size > INT_MAX)
        size = INT_MAX;
    int nwritten = static_cast<int>(size);

    if (offset > len)
    {
        if (chunkNumber(len) < chunksCount(len))
        {
            chunks[chunkNumber(len)].clearTail(chunkOffset(len));
        }
        len = size + offset;
    }
    else if (size > unsigned(len - offset))
    {
        len = size + offset;
    }
    chunks.resize(chunksCount(len));
    while (size > 0)
    {
        size_t w = chunks[chunk].write(buf, pos, size);

        size -= w;
        buf += w;
        ++chunk;
        pos = 0;
    }
    return nwritten;
}

int BigBuffer::file_write(const char *buf, size_t size, size_t offset, const char *real_name, uint64_t real_file_size)
{
    size_t chunk = chunkNumber(offset);
    unsigned int pos = chunkOffset(offset);
    if (size > INT_MAX)
        size = INT_MAX;
    int nwritten = static_cast<int>(size);

    if (offset > len)
    {
        if (chunkNumber(len) < chunksCount(len))
        {
            chunks[chunkNumber(len)].clearTail(chunkOffset(len));
        }
        len = size + offset;
    }
    else if (size > unsigned(len - offset))
    {
        len = size + offset;
    }
    chunks.resize(chunksCount(len));
    std::fstream real_file;
    real_file.open(real_name, std::ios::binary | std::ios::in);
    if (!real_file.is_open())
    {
        syslog(LOG_ERR, "failed to open %s", real_name);
        fprintf(stderr, "%s:failed to open %s\n", PROGRAM, real_name);
        return 0;
    }
    while (size > 0)
    {
        uint64_t real_offset = static_cast<uint64_t>(chunk * chunkSize);
        size_t w = chunks[chunk].file_write(buf, pos, size, real_file, real_offset, real_file_size, real_name);

        size -= w;
        buf += w;
        ++chunk;
        pos = 0;
    }
    real_file.close();
    return nwritten;
}

int BigBuffer::vsphere_write(const char *buf, size_t size, size_t offset, uint64_t real_file_size, std::vector<int> &real_file_vector, const int store_file, const std::unordered_map<int, std::vector<DataSegment>>& cache)
{
    //syslog(LOG_ERR, "[%ld]vsphere_write:%s at offset %lu size %lu\n",usecTime(), real_name, offset, size);
    size_t chunk = chunkNumber(offset);
    unsigned int pos = chunkOffset(offset);
    if (size > INT_MAX)
        size = INT_MAX;
    int nwritten = static_cast<int>(size);

    if (offset > len)
    {
        if (chunkNumber(len) < chunksCount(len))
        {
            chunks[chunkNumber(len)].clearTail(chunkOffset(len));
        }
        len = size + offset;
    }
    else if (size > unsigned(len - offset))
    {
        len = size + offset;
    }
    chunks.resize(chunksCount(len));

    while (size > 0)
    {
        uint64_t real_offset = static_cast<uint64_t>(chunk * chunkSize);
        size_t w = chunks[chunk].vsphere_write(buf, pos, size, real_file_vector, real_offset, real_file_size, store_file, cache);
        size -= w;
        buf += w;
        ++chunk;
        pos = 0;
    }

    //syslog(LOG_ERR, "[%ld]end write: %d bytes to %s at offset %lu size %lu\n",usecTime(), nwritten, real_name, offset, size);
    return nwritten;
}

int BigBuffer::fsphere_write(const char *buf, size_t size, size_t offset, const char *real_name, uint64_t real_file_size, const char *store_name)
{
    size_t chunk = chunkNumber(offset);
    unsigned int pos = chunkOffset(offset);
    if (size > INT_MAX)
        size = INT_MAX;
    int nwritten = static_cast<int>(size);

    if (offset > len)
    {
        if (chunkNumber(len) < chunksCount(len))
        {
            chunks[chunkNumber(len)].clearTail(chunkOffset(len));
        }
        len = size + offset;
    }
    else if (size > unsigned(len - offset))
    {
        len = size + offset;
    }
    chunks.resize(chunksCount(len));
    std::vector<int> real_file_vector;
    int real_file_count = open_disk_files(real_name, real_file_vector);
    if (real_file_count <= 0)
    {
        syslog(LOG_ERR, "fsphere_write:failed to open real_name %s", real_name);
        fprintf(stderr, "%s:fsphere_write:failed to open real_name %s\n", PROGRAM, real_name);
        return 0;
    }
    /*
    int real_file;
    real_file = open(real_name, O_RDONLY);
    if (real_file < 0)
    {
        syslog(LOG_ERR, "fsphere_write:failed to open real_name %s,with %s", real_name, strerror(errno));
        fprintf(stderr, "%s:fsphere_write:failed to open real_name %s,with %s\n", PROGRAM, real_name, strerror(errno));
        return 0;
    }
    */
    int store_file = 0;
    if(!param.memoryFlag)
    {
        if(strlen(param.storeDir) == 0)
        {
            syslog(LOG_ERR, "storeDir is not set");
            fprintf(stderr, "%s:storeDir is not set\n", PROGRAM);
            return 0;
        }
        if(strlen(store_name) == 0 || access(store_name, F_OK) != 0)
        {
            syslog(LOG_ERR, "failed to access %s", store_name);
            fprintf(stderr, "%s:failed to access %s\n", PROGRAM, store_name);
            return 0;
        }
        store_file = open(store_name, O_RDWR);
        if (store_file < 0)
        {
            syslog(LOG_ERR, "fsphere_write:failed to open store_name %s,with %s", store_name, strerror(errno));
            fprintf(stderr, "%s:fsphere_write:failed to open store_name %s,with %s\n", PROGRAM, store_name, strerror(errno));
            return 0;
        }
    }
    while (size > 0)
    {
        uint64_t real_offset = static_cast<uint64_t>(chunk * chunkSize);
        size_t w = chunks[chunk].fsphere_write(buf, pos, size, real_file_vector, real_offset, real_file_size, store_file);
        size -= w;
        buf += w;
        ++chunk;
        pos = 0;
    }
    /*
    if(real_file > 0)
        close(real_file);
    */
    for(int fd : real_file_vector)
    {
        if(fd > 0)
            close(fd);
    }
    if(store_file > 0)
        close(store_file);
    return nwritten;
}